﻿using System;
using System.Globalization;
using System.Text;
using System.Net;
using System.Xml.Serialization;

namespace Altairis.Sewen {

    [XmlType(TypeName = "page", Namespace = "http://schemas.altairis.cz/Sewen/Page/1.0/")]
    [XmlRoot(ElementName = "page", Namespace = "http://schemas.altairis.cz/Sewen/Page/1.0/")]
    public class SewenPage {
        private const string SLUG_WORD_SEPARATOR = "_";
        private const string SLUG_PATTERN = "^[0-9a-z_]{1,100}$";

        // Core properties

        /// <summary>
        /// Gets or sets the page title.
        /// </summary>
        /// <value>
        /// The title of page.
        /// </value>
        [XmlAttribute(AttributeName = "title")]
        public string Title { get; set; }

        /// <summary>
        /// Gets or sets the page body.
        /// </summary>
        /// <value>
        /// The body of the page.
        /// </value>
        [XmlElement(ElementName = "body")]
        public string Body { get; set; }

        /// <summary>
        /// Gets or sets the version timestamp.
        /// </summary>
        /// <value>
        /// The version timestamp.
        /// </value>
        [XmlAttribute(AttributeName = "version")]
        public DateTime Version { get; set; }

        /// <summary>
        /// Gets or sets the name of the user, who saved this particular version of page.
        /// </summary>
        /// <value>
        /// The name of the authenticated user or <c>null</c> for anonymous.
        /// </value>
        [XmlAttribute(AttributeName = "userName")]
        public string UserName { get; set; }

        /// <summary>
        /// Gets or sets the IP address from which this particular version of page was saved.
        /// </summary>
        /// <value>
        /// The IP address.
        /// </value>
        [XmlAttribute(AttributeName = "ipAddress")]
        public string IpAddress { get; set; }

        /// <summary>
        /// Gets or sets the comment entered during edit.
        /// </summary>
        /// <value>
        /// The edit comment.
        /// </value>
        [XmlElement(ElementName = "comment")]
        public string Comment { get; set; }

        // Other properties

        /// <summary>
        /// Gets the title formatted as slug.
        /// </summary>
        public string Slug {
            get {
                return GetSlug(this.Title);
            }
        }

        // Helper methods

        public void SetContextInfo() {
            var c = System.Web.HttpContext.Current;
            if (c == null) throw new NotSupportedException("This method is not supported when not running in context of web application.");

            if (c.Request.IsAuthenticated) {
                this.UserName = c.User.Identity.Name;
            }
            else {
                this.UserName = null;
            }
            this.IpAddress = c.Request.UserHostAddress;
        }

        /// <summary>
        /// Determines whether the specified string is valid slug.
        /// </summary>
        /// <param name="title">The title.</param>
        /// <returns>
        ///   <c>true</c> if the specified title is valid slug; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsSlug(string title) {
            if (title == null) throw new ArgumentNullException("title");
            if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "title");

            return System.Text.RegularExpressions.Regex.IsMatch(title, SLUG_PATTERN);
        }

        /// <summary>
        /// Gets valid slug based on page title.
        /// </summary>
        /// <param name="title">The page title.</param>
        /// <returns>Page title formatted as valid slug (containing only lowercase ASCII letters, numbers and underscore)</returns>
        public static string GetSlug(string title) {
            if (title == null) throw new ArgumentNullException("title");
            if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "title");
            if (IsSlug(title)) return title;

            title = title.Trim().ToLower();
            title = title.Normalize(NormalizationForm.FormD);
            var sb = new StringBuilder();
            var lastDash = true;

            for (int i = 0; i < title.Length; i++) {
                var cat = CharUnicodeInfo.GetUnicodeCategory(title[i]);
                switch (cat) {
                    case UnicodeCategory.LowercaseLetter:
                    case UnicodeCategory.DecimalDigitNumber:
                        // copy to output
                        sb.Append(title[i]);
                        lastDash = false;
                        break;
                    case UnicodeCategory.NonSpacingMark:
                        // ignore
                        break;
                    default:
                        // add word separator if not already there
                        if (!lastDash) sb.Append(SLUG_WORD_SEPARATOR);
                        lastDash = true;
                        break;
                }
                if (sb.Length == 100) break;
            }

            return sb.ToString().Normalize(NormalizationForm.FormC);
        }

    }

}
